#ifndef __CNCDEPS_HPP__
#define __CNCDEPS_HPP__

#include "CNCTask.hpp"
#include "../common/constants.h"
#include "../common/ComputePredsHelper.hpp"

#include "../simple_expression_template/VAR.hpp"
#include "../simple_expression_template/TERM.hpp"
#include "../simple_expression_template/EXPRESSION.hpp"
#include "../simple_expression_template/ADD.hpp"
#include "../simple_expression_template/SUB.hpp"
#include "../simple_expression_template/MIN.hpp"
#include "../simple_expression_template/MAX.hpp"
#include "../simple_expression_template/CEIL.hpp"
#include "../simple_expression_template/FLOOR.hpp"
#include "../simple_expression_template/GE.hpp"
#include "../simple_expression_template/LE.hpp"
#include "../simple_expression_template/FOR.hpp"

#include "../common/TupleHelper.hpp"
#include "../common/RuntimeWrapper.hpp"
#include "../common/DependenceGenerator.hpp"

template<typename CNC_CONTEXT, class ITEM_COLLECTION, class CNC_DC>
class CNCRuntimeWrapper : public RuntimeWrapper {
  CNC_DC &dc;
  CNC_CONTEXT &c;
  ITEM_COLLECTION &ic;

public:
  CNCRuntimeWrapper(CNC_CONTEXT &cc, ITEM_COLLECTION &icc, CNC_DC &dcc) : 
    dc(dcc), c(cc), ic(icc) {}

  // No async get in CnC, therefore, no need to copy the tag and have a
  // copy_tag != this_tag 
  template <typename TAG_TYPE>
  static TAG_TYPE &makeAsyncTagCopy(TAG_TYPE &this_tag) {
    return this_tag;
  }

  // Async get in SWARM, therefore, need to copy the tag and have a
  // copy_tag != this_tag 
  template <typename TAG_TYPE>
  void get(TAG_TYPE &t, TAG_TYPE &copy_t, void *&res) {
    assert(&t == &copy_t);
    get(t, res);
  }

  template <typename TAG_TYPE>
  void get(TAG_TYPE &t, void *&res) {
#ifdef MEASURE_OVERHEADS
    Timer oh(GlobalTimers::Singleton().getOverheadCNCGetPut());
#endif // MEASURE_OVERHEADS
    ic.get(t, res);
  }

  template <typename TAG_TYPE>
  void getZero(TAG_TYPE &t, void *&res) {
#ifdef MEASURE_OVERHEADS
    Timer oh(GlobalTimers::Singleton().getOverheadCNCGetPut());
#endif // MEASURE_OVERHEADS
    TAG_TYPE tt(toArray(makeIdenticalTuple<tag_elem_t, t.tasktagdim>::make(ZERO_TASK_INDEX)));
    ic.get(tt, res);
  }

  template <typename TAG_TYPE>
  void depends(TAG_TYPE &t) {
#ifdef MEASURE_OVERHEADS
    Timer oh(GlobalTimers::Singleton().getOverheadCNCDep());
#endif // MEASURE_OVERHEADS
    dc.depends(ic, t);
  }

  template <typename TAG_TYPE>
  void dependsZero(TAG_TYPE &) {
#ifdef MEASURE_OVERHEADS
    Timer oh(GlobalTimers::Singleton().getOverheadCNCDep());
#endif // MEASURE_OVERHEADS
    TAG_TYPE tt(toArray(makeIdenticalTuple<tag_elem_t, TAG_TYPE::tasktagdim>::make(ZERO_TASK_INDEX)));
    dc.depends(ic, tt);
  }

  // No async gets in CnC
  template<typename TAG_TYPE>
  bool markedAsyncGet(TAG_TYPE &, int) {
    return false;
  }

  // No async gets in CnC
  bool markedAsyncGet() {
    return false;
  }
};

template<int TASK_ID, int TASKTAGDIM, int INTERTASKDIM> 
using CNCDeps = DependenceGenerator<TASK_ID, TASKTAGDIM, INTERTASKDIM, INTERTASKDIM>;
#endif // __CNCDEPS_HPP__
